#include "udpagent.h"
#include "error.h"

#define	SA	struct sockaddr

UdpAgent::UdpAgent()
{
  if ( (m_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	cerr<<"socket error";
  m_bConnected = false;
}

UdpAgent::UdpAgent(int cs)
{
  m_socket = cs;
  m_bConnected = false;
}


UdpAgent::~UdpAgent()
{
  close(m_socket);
}

void UdpAgent::SetNonBlocking()
{
  int val;
  val = fcntl(m_socket, F_GETFL, 0);
  fcntl(m_socket, F_SETFL, val | O_NONBLOCK);
}

int UdpAgent::Bind(const unsigned short port)
{

  struct sockaddr_in	hostaddr;

  bzero(&hostaddr, sizeof(hostaddr));
  hostaddr.sin_family = AF_INET;
  hostaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  hostaddr.sin_port = htons(port);
  int res;
  if ((res = bind(m_socket, (SA *)&hostaddr, sizeof(hostaddr))) < 0)
	{
	  perror("Bind");
	  return res;
	}
  return 0;
}

int UdpAgent::Bind(const string ip, const unsigned short port)
{

  struct sockaddr_in	hostaddr;

  bzero(&hostaddr, sizeof(hostaddr));
  hostaddr.sin_family = AF_INET;
  hostaddr.sin_addr.s_addr = inet_addr(ip.c_str());
  hostaddr.sin_port = htons(port);
  int res;
  if ((res = bind(m_socket, (SA *)&hostaddr, sizeof(hostaddr))) < 0)
	{
	  perror("Bind");
	  return res;
	}
  return 0;
}

int UdpAgent::Connect(const string& ipaddress, const unsigned short port)
{
  struct sockaddr_in	servaddr;

  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons(port);
  servaddr.sin_addr.s_addr = inet_addr(ipaddress.c_str());

  if (connect(m_socket, (SA *)&servaddr, sizeof(servaddr)) < 0)
	perror("Connect");

  m_bConnected = true;
  return 0;
}

int UdpAgent::Connect(const struct sockaddr_in& addr)
{
  if (connect(m_socket, (SA *)&addr, sizeof(addr)) < 0)
	cerr<<("connect error");

  m_bConnected = true;
  return 0;
  
}

int UdpAgent::SendTo(const char * buf, const unsigned long length, 
		     const string& ipaddress, const unsigned short port)
{
  if (ipaddress == "")
    return(write(m_socket, buf, length)); // socket must be bound and connected
  int n;
  struct sockaddr_in	hostaddr;
  bzero(&hostaddr, sizeof(hostaddr));
  hostaddr.sin_family = AF_INET;
  hostaddr.sin_port = htons(port);
  if ( (n = inet_pton(AF_INET, ipaddress.c_str(), &hostaddr.sin_addr)) < 0)
	cerr<<"inet_pton error for "<<ipaddress.c_str();	/* errno set */
  return (sendto(m_socket, buf, length, 0, (struct sockaddr *)&hostaddr, sizeof(hostaddr)));
}

int UdpAgent::SendTo(const string& buf, 
		   const string& ipaddress, const unsigned short port)
{
  sockaddr_in RecvAddr;

  RecvAddr.sin_family = AF_INET;
  RecvAddr.sin_port = htons(port);
  RecvAddr.sin_addr.s_addr = inet_addr(ipaddress.c_str());

  return(sendto(m_socket, buf.c_str(), buf.size(), 0,
				(struct sockaddr *)&RecvAddr, sizeof(RecvAddr)));
}

// for connected socket, hostaddr must be NULL
int UdpAgent::SendTo(const char * buf, const unsigned long length, 
		     const struct sockaddr_in& hostaddr)
{
  return (sendto(m_socket, buf, length, 0, (struct sockaddr *)&hostaddr, sizeof(hostaddr)));
}

int UdpAgent::SendTo(const string& buf,
		     const struct sockaddr_in& hostaddr)
{
  return (sendto(m_socket, buf.c_str(), buf.size(), 0, (struct sockaddr *)&hostaddr, sizeof(hostaddr)));
}


// length is the maximum allocated size for ptr

int UdpAgent::RecvFrom(char * ptr, const unsigned long length, struct sockaddr_in& from)
{
  socklen_t len = sizeof(from);
  return(recvfrom(m_socket, ptr, length, 0, (struct sockaddr *)&from, &len));
}

int UdpAgent::RecvFrom(string& buf, struct sockaddr_in& from)
{
  int n;
  n = RecvFrom(m_buf, MAX_BUF-1, from);
  assert (n < MAX_BUF);
  buf.assign( m_buf, n );
  return n;
}

int UdpAgent::Send(const char * buf, size_t len)
{
  assert(m_bConnected);
  return (send(m_socket, buf, len, 0));
}

int UdpAgent::Recv(char * buf, size_t len)
{
  assert(m_bConnected);
  int n;
  n = recv(m_socket, buf, len, 0);
  return n;
}

// nusec if 0, return immediately, if -1, block infinitely
int UdpAgent::IsReadable(int nusec)
{
  fd_set rset;
  struct timeval tval;

  FD_ZERO(&rset);
  FD_SET(m_socket, &rset);
  tval.tv_sec = 0;
  tval.tv_usec = nusec;			// 10^-6
  select(m_socket+1, &rset, NULL, NULL, nusec==-1 ? NULL : &tval);
  return FD_ISSET(m_socket, &rset);
}
